package com.gylang.springmini.registry;

import cn.hutool.core.util.StrUtil;
import com.gylang.springmini.factory.ObjectFactory;
import com.gylang.springmini.support.BeanDefinition;
import org.springframework.lang.Nullable;

import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author gylang
 * data 2021/11/15
 */
public abstract class SimpleSingletonBeanRegistry extends SimpleBeanDefinitionRegistry implements SingletonBeanRegistry {

    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
    private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>();
    private final Map<String, ObjectFactory<?>> singletonFactories = new ConcurrentHashMap<>();
    private Set<String> singletonCurrentlyInCreation = new HashSet<>();

    @Override
    public void registerSingleton(String beanName, Object singletonObject) {
        singletonObjects.put(beanName, singletonObject);
    }

    @Override
    public Object getSingleton(String beanName) {
        return getSingleton(beanName, true);
    }

    @Nullable
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        // Quick check for existing instance without full singleton lock
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                synchronized (this.singletonObjects) {
                    // Consistent creation of early reference within full singleton lock
                    singletonObject = this.singletonObjects.get(beanName);
                    if (singletonObject == null) {
                        singletonObject = this.earlySingletonObjects.get(beanName);
                        if (singletonObject == null) {
                            ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                            if (singletonFactory != null) {
                                singletonObject = singletonFactory.getObject();
                                this.earlySingletonObjects.put(beanName, singletonObject);
                                this.singletonFactories.remove(beanName);
                            }
                        }
                    }
                }
            }
        }
        return singletonObject;
    }

    public boolean isSingletonCurrentlyInCreation(String beanName) {
        return singletonCurrentlyInCreation.contains(beanName);
    }


    public boolean beforeSingletonCurrentlyInCreation(String beanName) {
        return singletonCurrentlyInCreation.add(beanName);
    }

    public boolean afterSingletonCurrentlyInCreation(String beanName) {
        return singletonCurrentlyInCreation.remove(beanName);
    }

    @Override
    public BeanDefinition mergeBeanDefinition(String beanName) {

        // 合并的
        BeanDefinition beanDefinition = getBeanDefinition(beanName);
        // 获取最上层为实现类
        if (null == beanDefinition) {

            return null;
        }

        beanDefinition = beanDefinition.clone();
        while (true) {
            String parentName = beanDefinition.getParentName();

            if (StrUtil.isEmpty(parentName)) {
                break;
            }
            BeanDefinition parent = getBeanDefinition(parentName);
            if (null == parent) {
                break;
            }
            // 合并父类属性
            beanDefinition.mergeSuperBeanDefinition(parent);
        }
        return beanDefinition;
    }

    @Override
    public boolean containsSingleton(String beanName) {
        return this.singletonObjects.containsKey(beanName);
    }

    @Override
    public String[] getSingletonNames() {
        synchronized (this.singletonObjects) {
            return singletonObjects.keySet().toArray(new String[0]);
        }
    }

    @Override
    public int getSingletonCount() {
        synchronized (this.singletonObjects) {

            return singletonObjects.size();
        }
    }

    @Override
    public Object getSingletonMutex() {
        return singletonObjects;
    }
}
